﻿using Candy.Model;
using Candy.Web.Data;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Threading;
using Candy.Infrastructure;

namespace Candy.ViewModel
{
    public class JobManagerViewModel : CandyViewModelBase
    {
        private bool _isBusy;
        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }
            set
            {
                //DispatcherHelper.CheckBeginInvokeOnUI(() =>
                //    {
                _isBusy = value;
                if (_isBusy)
                    base.ReportProgress();
                else
                    base.FinishProgress();
                RaisePropertyChanged("IsBusy");
                //     });
            }
        }


        private ObservableCollection<JobGroupViewModel> _jobs = new ObservableCollection<JobGroupViewModel>();
        public ObservableCollection<JobGroupViewModel> Jobs
        {
            get
            {
                return _jobs;
            }
            set
            {
                _jobs = value;
                RaisePropertyChanged("Jobs");
            }
        }

        private JobViewModel _selectedJob;
        public JobViewModel SelectedJob
        {
            get
            {
                return _selectedJob;
            }
            set
            {
                if (_selectedJob != value)
                {
                    _selectedJob = value;
                    RaisePropertyChanged("SelectedJob");
                    if (_selectedJob != null)
                    {
                        if (_selectedJob.LoadHits.CanExecute(null))
                            _selectedJob.LoadHits.Execute(null);
                    }

                }
            }
        }

        private BlastSecondaryBaitsViewModel _blastSecondaryBaitsVM;
        //public BlastSecondaryBaitsViewModel BlastSecondaryBaitsVM
        //{
        //    get
        //    {
        //        return _blastSecondaryBaitsVM;
        //    }
        //    set
        //    {
        //        _blastSecondaryBaitsVM = value;
        //        RaisePropertyChanged("BlastSecondaryBaitsVM");
        //    }
        //}



        DispatcherTimer _jobChecker;
        IDataService _dataService;
        INavigationService _navigationService;
        public JobManagerViewModel(IDataService dataService, INavigationService navigationService)
        {
            _navigationService = navigationService;
            _dataService = dataService;
            if (IsInDesignMode)
            {

                Jobs = new ObservableCollection<JobGroupViewModel>();

                // Guid g = Guid.NewGuid();
                List<BlastJobDto> jobs = new List<BlastJobDto>()
                {
                    new BlastJobDto { Name = "Job A", FamilyId = 1},
                    new BlastJobDto { Name = "Job B", FamilyId = 2},
                    new BlastJobDto { Name = "Job C", FamilyId = 3},
                    new BlastJobDto { Name = "Job D", FamilyId = 4},
                };
                JobGroupViewModel j = new JobGroupViewModel(_dataService, jobs);
                Jobs.Add(j);
            }
            else
            {
                _dataService.DataContext.BlastJobDtos.EntityAdded += BlastJobDtos_EntityAdded;
                if (_jobChecker == null)
                    _jobChecker = new DispatcherTimer();
                _jobChecker.Tick += (s, a) =>
                    {
                        _jobChecker.Interval = TimeSpan.FromSeconds(6);
                        if (!IsBusy)
                        {
                            CheckJobStatus();
                        }
                    };
                _jobChecker.Interval = TimeSpan.FromSeconds(6);

            }
        }

        void BlastJobDtos_EntityAdded(object sender, EntityCollectionChangedEventArgs<BlastJobDto> e)
        {
            MergeInNewJobs(e.Entity);
        }



        private RelayCommand<object> _deleteJob;
        public RelayCommand<object> DeleteJob
        {
            get
            {
                return _deleteJob ?? (_deleteJob = new RelayCommand<object>(ExecDeleteJob, CanDeleteJob));
            }
        }

        /// <summary>
        /// Checks whether the DeleteJob command is executable
        /// </summary>
        private bool CanDeleteJob(object val)
        {
            return true;
        }

        /// <summary>
        /// Executes the DeleteJob command 
        /// </summary>
        private void ExecDeleteJob(object val)
        {
            if (val is JobViewModel)
            {
                IsBusy = true;
                JobViewModel job = val as JobViewModel;
                _dataService.DataContext.BlastJobDtos.Remove(job.Entity);
                _dataService.SubmitAsync(op =>
                    {
                        IsBusy = false;
                        if (op.HasError)
                        {
                            base.HandleDomainError(op, "Deleting job failed.");
                        }
                        else
                        {
                            JobGroupViewModel groupToDelete = null;
                            foreach (var item in this.Jobs)
                            {
                                var toDelete = item.Jobs.SingleOrDefault(x => x.Entity == job.Entity);
                                if (toDelete != null)
                                {
                                    item.Jobs.Remove(toDelete);
                                    if (item.Jobs.Count == 0)
                                        groupToDelete = item;
                                    break;
                                }
                            }
                            if (groupToDelete != null)
                                Jobs.Remove(groupToDelete);
                        }

                    });

            }
            if (val is JobGroupViewModel)
            {
                IsBusy = true;
                JobGroupViewModel group = val as JobGroupViewModel;
                foreach (var item in group.Jobs)
                {
                    _dataService.DataContext.BlastJobDtos.Remove(item.Entity);
                }
                _dataService.SubmitAsync(op =>
                  {
                      IsBusy = false;
                      if (op.HasError)
                      {
                          base.HandleDomainError(op, "Deleting job failed.");
                      }
                      else
                      {
                          Jobs.Remove(group);
                      }

                  });
            }
        }


        private bool _firstJobLoad = true;
        private void CheckJobStatus()
        {

            base.ResetError();
            //DataAccess.Instance.RefreshJobRequests();
            EntityQuery<BlastJobDto> q;
            if (_firstJobLoad)
            {
                IsBusy = true;
                //   q = DataAccess.Instance.DataContext.GetJobsQuery(DataAccess.Instance.Project.Id);
                _dataService.LoadJobsAsync(op =>
                    {
                        IsBusy = false;
                        if (!op.HasError)
                        {
                            _firstJobLoad = false;
                         //   Jobs = new ObservableCollection<JobGroupViewModel>();
                            //var famJobIds = op.Entities.Where(x => x.Task == BlastTask.blast_fishing_dbs).Select(x => x.FamilyId.Value).Distinct();
                            //var baitJobIds = op.Entities.Where(x => x.Task == BlastTask.find_ortholog_genes).Select(x => x.BaitId.Value).Distinct();
                            ////DispatcherHelper.CheckBeginInvokeOnUI(() =>
                            ////     {
                            //foreach (var item in famJobIds)
                            //{
                            //    JobGroupViewModel jobGroup = new JobGroupViewModel(_dataService, op.Entities.Where(x => x.FamilyId == item && x.Task == BlastTask.blast_fishing_dbs).OrderBy(x => x.DateTime));
                            //    Jobs.Add(jobGroup);
                            //}
                            //foreach (var item in baitJobIds)
                            //{
                            //    JobGroupViewModel jobGroup = new JobGroupViewModel(_dataService, op.Entities.Where(x => x.BaitId == item && x.Task == BlastTask.find_ortholog_genes).OrderBy(x => x.DateTime));
                            //    Jobs.Add(jobGroup);
                            //}
                        }
                        else
                        {
                            base.HandleDomainError(op, "Retrieving BLAST jobs failed.");
                        }
                    });
            }
            else
            {
                List<BlastJobDto> jobsToRefresh = new List<BlastJobDto>();

                foreach (var item in Jobs)
                {
                    jobsToRefresh.AddRange(item.Jobs.Select(x => x.Entity));
                }
                if (jobsToRefresh.Count == 0)
                {
                    return;
                }
                IsBusy = true;
                _dataService.RefreshJobsAsync(op =>
                 {
                     IsBusy = false;
                     if (!op.HasError)
                     {
                        // MergeInNewJobs(op.Entities);
                     }
                     else
                     {
                         base.HandleDomainError(op, "Retrieving BLAST jobs failed.");
                     }
                 }, jobsToRefresh);

            }
        }

        private void MergeInNewJobs(IEnumerable<BlastJobDto> jobs)
        {

            foreach (var item in jobs)
            {
                MergeInNewJobs(item);
            }
        }

        private void MergeInNewJobs(BlastJobDto item)
        {
            if (item.EntityState == EntityState.Detached)
                return;
            if (item.Task == BlastTask.blast_fishing_dbs)
            {
                JobGroupViewModel jobGroup = Jobs.SingleOrDefault(x => x.GroupMode == JobGroupViewModel.Mode.Family && x.GroupId == item.FamilyId);
                if (jobGroup == null)
                {
                    jobGroup = new JobGroupViewModel(_dataService, item.FamilyName, item.FamilyId.Value, JobGroupViewModel.Mode.Family);
                    Jobs.Add(jobGroup);
                }
                if (jobGroup.Jobs.Any(x => x.Entity.Id == item.Id) == false)
                    jobGroup.Jobs.Add(new JobViewModel(_dataService, item));
            }
            if (item.Task == BlastTask.find_ortholog_genes)
            {
                JobGroupViewModel jobGroup = Jobs.SingleOrDefault(x => x.GroupMode == JobGroupViewModel.Mode.Bait && x.GroupId == item.BaitId);
                if (jobGroup == null)
                {
                    jobGroup = new JobGroupViewModel(_dataService, item.BaitName, item.BaitId.Value, JobGroupViewModel.Mode.Bait);
                    Jobs.Add(jobGroup);
                }
                if (jobGroup.Jobs.Any(x => x.Entity.Id == item.Id) == false)
                    jobGroup.Jobs.Add(new JobViewModel(_dataService, item));
            }
        }



        private RelayCommand _enableAutoRefesh;
        public RelayCommand EnableAutoRefresh
        {
            get
            {
                return _enableAutoRefesh ?? (_enableAutoRefesh = new RelayCommand(ExecEnableAutoRefresh, CanEnableAutoRefresh));
            }
        }

        /// <summary>
        /// Checks whether the EnableAutoRefresh command is executable
        /// </summary>
        private bool CanEnableAutoRefresh()
        {
            return true;
        }

        /// <summary>
        /// Executes the EnableAutoRefresh command 
        /// </summary>
        private void ExecEnableAutoRefresh()
        {
            _jobChecker.Interval = TimeSpan.FromMilliseconds(1);
            _jobChecker.Start();
        }



        private RelayCommand _disableAutoRefresh;
        public RelayCommand DisableAutoRefresh
        {
            get
            {
                return _disableAutoRefresh ?? (_disableAutoRefresh = new RelayCommand(ExecDisableAutoRefresh, CanDisableAutoRefresh));
            }
        }

        /// <summary>
        /// Checks whether the DisableAutoRefresh command is executable
        /// </summary>
        private bool CanDisableAutoRefresh()
        {
            return true;
        }

        /// <summary>
        /// Executes the DisableAutoRefresh command 
        /// </summary>
        private void ExecDisableAutoRefresh()
        {
            _jobChecker.Stop();
        }



        private RelayCommand<BlastJobDefinitionDto> _createJob;
        public RelayCommand<BlastJobDefinitionDto> CreateJob
        {
            get
            {
                return _createJob ?? (_createJob = new RelayCommand<BlastJobDefinitionDto>(ExecCreateJob, CanCreateJob));
            }
        }

        /// <summary>
        /// Checks whether the CreateJob command is executable
        /// </summary>
        private bool CanCreateJob(BlastJobDefinitionDto val)
        {
            return true;
        }

        /// <summary>
        /// Executes the CreateJob command 
        /// </summary>
        private void ExecCreateJob(BlastJobDefinitionDto val)
        {
            base.ResetError();

            base.ConfirmAction(doit =>
                {
                    if (doit)
                    {
                        IsBusy = true;
                        _dataService.CreateJobAsync(op =>
                             {
                                 IsBusy = false;
                                 if (op.HasError)
                                 {
                                     base.HandleDomainError(op, "Creating BLAST job failed.");
                                 }
                                 else
                                 {
                                     if (_firstJobLoad != true)
                                         MergeInNewJobs(op.Value);
                                 }
                             }, val);
                    }
                }, "Blast?");


        }


        private RelayCommand<List<BlastJobDefinitionDto>> _createJobs;
        public RelayCommand<List<BlastJobDefinitionDto>> CreateJobs
        {
            get
            {
                return _createJobs ?? (_createJobs = new RelayCommand<List<BlastJobDefinitionDto>>(ExecCreateJobs, CanCreateJobs));
            }
        }

        /// <summary>
        /// Checks whether the CreateJobs command is executable
        /// </summary>
        private bool CanCreateJobs(List<BlastJobDefinitionDto> val)
        {
            return val != null && val.Count > 0;
        }

        /// <summary>
        /// Executes the CreateJobs command 
        /// </summary>
        private void ExecCreateJobs(List<BlastJobDefinitionDto> val)
        {
            base.ResetError();

            //base.ConfirmAction(doit =>
            //{
            //    if (doit)
            //    {
                    IsBusy = true;
                    _dataService.CreateJobsAsync(op =>
                    {
                        IsBusy = false;
                        if (op.HasError)
                        {
                            base.HandleDomainError(op, "Creating BLAST jobs failed.");
                        }
                        else
                        {
                            if (_firstJobLoad != true)
                                MergeInNewJobs(op.Value);
                        }
                    }, val);
            //    }
            //}, "Blast " + val.Count + " gene(s)?");
        }




        private RelayCommand<IEnumerable<GeneDto>> _blastSecondaryBaits;
        public RelayCommand<IEnumerable<GeneDto>> BlastSecondaryBaits
        {
            get
            {
                return _blastSecondaryBaits ?? (_blastSecondaryBaits = new RelayCommand<IEnumerable<GeneDto>>(ExecBlastSecondaryBaits, CanBlastSecondaryBaits));
            }
        }

        /// <summary>
        /// Checks whether the BlastSecondaryBaits command is executable
        /// </summary>
        private bool CanBlastSecondaryBaits(IEnumerable<GeneDto> val)
        {
            return val != null && val.Count() > 0;
        }

        /// <summary>
        /// Executes the BlastSecondaryBaits command 
        /// </summary>
        private void ExecBlastSecondaryBaits(IEnumerable<GeneDto> val)
        {
            _blastSecondaryBaitsVM = new BlastSecondaryBaitsViewModel(_dataService);
            _blastSecondaryBaitsVM.LoadDatabases.Execute(null);
            _blastSecondaryBaitsVM.GenesToBlast = new ObservableCollection<GeneDto>(val);
            _navigationService.ShowSecondaryBlast(_blastSecondaryBaitsVM);
        }





    }

    public enum ImportStatus
    {
        READY,
        OK,
        DOUBLEENTRY,
        IMPORTING
    }
}
